﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.Linq;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Dynamic;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using IntraVision.Xml;

namespace IntraVision.Web
{
    /// <summary>
    /// Контроллер для вывода списка сущностей, просмотра, редактирования, создания и удаления сущностей
    /// </summary>
    /// <typeparam name="TEntity">Тип сущности</typeparam>
    /// <typeparam name="TDataContext">Тип контекста</typeparam>
    public class EntityListController<TEntity, TDataContext> : ReadOnlyEntityController<TEntity, TDataContext>
        where TEntity : class, new()
        where TDataContext : DataContext, new()
    {
        protected bool NewEntity { get; set; }

        protected virtual void BeforeEntityInitialized(TDataContext dc, TEntity entity) { }
        protected virtual void BeforeSubmit(TDataContext dc, TEntity entity) { }
        protected virtual void AfterSubmit(TDataContext dc, TEntity entity) { }
        protected virtual void EntityDeleted(TDataContext dc, List<TEntity> deleted) { }

        /// <summary>
        /// Открытие карточки сущности на создание
        /// </summary>
        public virtual void Create()
        {
            if (string.IsNullOrEmpty(TemplatePath)) TemplatePath = "~/views/" + Name + "/form.haml";
            if (string.IsNullOrEmpty(LayoutPath) && string.IsNullOrEmpty(Request["nolayout"])) LayoutPath = "~/views/layouts/page.haml";

            using (var dc = new TDataContext())
            {
                TEntity entity = new TEntity();

                EntityLoaded(dc, entity);

                if (!string.IsNullOrEmpty(Request["json"]))
                    RenderJSON(entity);
                else
                {
                    ViewData["entity"] = entity;
                    RegisterFormScripts();
                    RenderView();
                }
            }
        }

        /// <summary>
        /// Просмотр карточки сущности
        /// </summary>
        /// <param name="entityid"></param>
        public override void View(string entityid)
        {
            RegisterFormScripts();
            base.View(entityid);
        }

        /// <summary>
        /// Регистрация стандартных для формы скриптов и стилей
        /// </summary>
        public virtual void RegisterFormScripts()
        {
            //Регистрация стандартных для формы скриптов
            RegisterScript("form.js", "jquery.validate.pack.js", "jquery.validate.additional.js");
            if (Language.ToUpper() != "EN") RegisterScript(string.Format("jquery.validate.messages-{0}.js", Language));

            //Добавление скрипта с именем ~/js/<ControllerName>/form.js, если он существует
            string jspath = Server.MapPath(string.Format(CultureInfo.InvariantCulture, "~/js/{0}/form.js", Name));
            if (File.Exists(jspath))
                RegisterScript(string.Format(CultureInfo.InvariantCulture, "{0}/form.js", Name));

            //Добавление стиля с именем ~/css/<ControllerName>/form.css, если он существует
            string csspath = Server.MapPath(string.Format(CultureInfo.InvariantCulture, "~/css/{0}/form.css", Name));
            if (File.Exists(csspath))
                RegisterStylesheet(string.Format(CultureInfo.InvariantCulture, "{0}/form.css", Name));
        }

        /// <summary>
        /// Сохранение сущности
        /// </summary>
        public virtual void Save()
        {
            using (var dc = new TDataContext())
            {
                int id = 0;
                TEntity entity = null;
                try
                {
                    if (!string.IsNullOrEmpty(Request["id"]) && !Int32.TryParse(Request["id"], out id))
                        throw new Exception("Неправильный идентификатор");

                    if (id > 0)
                    {
                        entity = dc.GetTable<TEntity>().Where("Id = @0", id).FirstOrDefault();
                        BeforeEntityInitialized(dc, entity);
                        RequestHelper.InitializeFromRequest<TEntity>(Request, entity);
                    }
                    else
                    {
                        entity = new TEntity();
                        BeforeEntityInitialized(dc, entity);
                        RequestHelper.InitializeFromRequest<TEntity>(Request, entity);
                        dc.GetTable<TEntity>().InsertOnSubmit(entity);

                        NewEntity = true;
                    }

                    BeforeSubmit(dc, entity);

                    var cs = dc.GetChangeSet();
                    if (cs.Updates.Count > 0 || cs.Inserts.Count > 0 || cs.Deletes.Count > 0)
                        dc.SubmitChanges();

                    AfterSubmit(dc, entity);

                    Session[Name + "_changed"] = id;
                    ViewData["postback"] = true;
                }
                catch (IntraVisionWebException ex)
                {
                    throw ex;
                }
                catch (SqlException ex)
                {
                    //Замена стандартного SQL сообщения об ошибке на сообщение об ошибке из файла customerrors.sql
                    var wex = SqlExceptionHelper.WrapException(ex);
                    if (!string.IsNullOrEmpty(Request["json"]))
                    {
                        RenderJSON(new AJAXResult(wex));
                        return;
                    }
                    else
                        ViewData.Exceptions.Add(wex);
                }
                catch (Exception ex)
                {
                    if (!string.IsNullOrEmpty(Request["json"]))
                    {
                        RenderJSON(new AJAXResult(ex));
                        return;
                    }
                    else
                        ViewData.Exceptions.Add(ex);
                }

                EntityLoaded(dc, entity);
                if (ViewData.Exceptions.Count == 0)
                {
                    if (!string.IsNullOrEmpty(Request["closeaftersave"]))
                        ViewData["closeaftersave"] = true;
                    if (!string.IsNullOrEmpty(Request["returnaftersave"]))
                    {
                        ReturnAfterSave(Request["returnaftersave"]);
                        return;
                    }
                }

                if (!string.IsNullOrEmpty(Request["json"]))
                    RenderJSON(entity);
                else
                {
                    RegisterFormScripts();

                    //Восстановить текущую закладку
                    if (!string.IsNullOrEmpty(Request["tab"])) ViewData["tab"] = Request["tab"];

                    ViewData["entity"] = entity;
                    if (string.IsNullOrEmpty(TemplatePath)) TemplatePath = "~/views/" + Name + "/form.haml";
                    if (string.IsNullOrEmpty(LayoutPath)) LayoutPath = "~/views/layouts/page.haml";
                    RenderView();
                }
            }
        }

        protected virtual void ReturnAfterSave()
        {
            ReturnAfterSave("TRUE");
        }

        protected virtual void ReturnAfterSave(string page)
        {
            if (page.ToUpper() == "TRUE")
                RedirectToAction("List", null);
            else
                Response.Redirect(page);
        }

        public virtual void Delete()
        {
            using (var dc = new TDataContext())
            {
                try
                {
                    if (String.IsNullOrEmpty(Request["id"]))
                        throw new ArgumentException("Идентификатор записи не передан");

                    int[] ids = null;
                    if (Request["id"].IndexOf(',') > 0)
                        ids = RequestHelper.GetIdsFromString(Request["id"]);
                    else
                    {
                        ids = new int[1];
                        if (!Int32.TryParse(Request["id"], out ids[0]))
                            throw new ArgumentException("Неправильный идентификатор");
                    }

                    List<TEntity> deleted = new List<TEntity>();

                    var table = dc.GetTable<TEntity>();
                    foreach (int i in ids)
                    {
                        TEntity entity = table.Where("Id = @0", i).FirstOrDefault();
                        if (entity != null)
                        {
                            deleted.Add(entity);
                            dc.GetTable<TEntity>().DeleteOnSubmit(entity);
                        }
                    }

                    EntityDeleted(dc, deleted);

                    if (dc.GetChangeSet().Deletes.Count > 0)
                        dc.SubmitChanges();
                }
                catch (SqlException ex)
                {
                    var wex = SqlExceptionHelper.WrapException(ex);
                    if (!string.IsNullOrEmpty(Request["json"]))
                    {
                        RenderJSON(new AJAXResult(wex));
                        return;
                    }
                    else
                        ViewData.Exceptions.Add(wex);
                }
                catch (Exception ex)
                {
                    if (!string.IsNullOrEmpty(Request["json"]))
                    {
                        RenderJSON(new AJAXResult(ex));
                        return;
                    }
                    else
                        ViewData.Exceptions.Add(ex);
                }

                if (!string.IsNullOrEmpty(Request["json"]))
                {
                    AJAXResult result = new AJAXResult();
                    result.Id = Request["id"];
                    RenderJSON(result);
                }
                else
                    List();
            }
        }
    }
}